/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  djc_socket_server.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年02月28日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年02月28日 13时59分51秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <ctype.h>
#include <libgen.h>
#include <sys/resource.h>
#include <pthread.h>

#define MAX_EVENTS 512
#define ARRAY_SIZE(X)   (sizeof(x)/sizeof(x[0]))
void set_socket_rlimit(void);
int socket_Server_init(char *listen_ip,int listen_port);
void print_usage(char *progname)
{
     printf("%s usage: \n", progname);
     printf("-p(--port): sepcify server listen port.\n");
     printf("-h(--Help): print this help information.\n");
     printf("-d(--daemon):set program running on background\n");
     return ;
}

int main (int argc, char **argv)
{

    int                 listenfd = -1;
    int                 clifd;
    struct sockaddr_in  servaddr;
    struct sockaddr_in  cliaddr;
    int                serv_port = 0;
    int                 rv ;    
    int                 rdest;
    char                buf[1024];
    int                 i,j;
    int                 found;
    int                 ch;   
    int                 daemon_run = 0;
    char                *progname = NULL;

    int                 epollfd;
    struct epoll_event  event;
    struct epoll_event  event_array[MAX_EVENTS];
    int                 events;
struct option opts[] =
{
     {"daemon",no_argument,NULL,'b'},
     {"port", required_argument, NULL, 'p'},
     {"help", no_argument, NULL, 'h'},
     {NULL, 0, NULL, 0}
};
progname = basename(argv[0]);

 while( (ch=getopt_long(argc, argv, "bp:h", opts, NULL)) != -1 )
      {
           switch(ch)
                {
                    case 'p':
                        serv_port=atoi(optarg);
                        break;
                    case 'b':
                        daemon_run=1;
                        break;
                    case 'h':
                        print_usage(argv[0]);
                        return 0;
                }
    }
  if( !serv_port )
  {
      print_usage(argv[0]);
      return 0;
  }
    set_socket_rlimit();

    if( (listenfd = socket_Server_init(NULL, serv_port)) <0)
    {
        printf("ERROR %s server listen on port %d failure\n",argv[0],serv_port);
        return -1;
    }

    printf("%s server start to listen on port %d\n",argv[0],serv_port);

    
    if(daemon_run)//设置进程后台运行
    {
        daemon(0,0);
    }
    
    if( (epollfd =epoll_create(MAX_EVENTS) ) <0 )
    {
        printf("epoll_create failure:%s\n",strerror(errno));
        return -3;
    }
    event.events = EPOLLIN;
    event.data.fd =listenfd;
    
    if(epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event) < 0)
    {
        printf("epoll add listen socket failure:%s\n",strerror(errno));
        return -4;
    }
    for( ; ; )
    {
        //program will blocked here
        events=epoll_wait(epollfd,event_array,MAX_EVENTS,-1);
        if(events < 0)
        {
            printf("epoll failure:%s\n",strerror(errno));
            break;
        }
        else if(events == 0)
        {
            printf("epoll get timeout\n");
            continue;
        }
        for(i=0; i<events; i++)
        {
            if( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP))
            {
                printf("epoll_wait get error on fd[%d]:%s\n",event_array[i].data.fd,strerror(errno));
                epoll_ctl(epollfd,EPOLL_CTL_DEL,event_array[i].data.fd,NULL);
                close(event_array[i].data.fd);
            }
        
            if( event_array[i].data.fd == listenfd )
            {
                if( (clifd=accept(listenfd, (struct sockaddr *)NULL, NULL)) < 0)
                {
                    printf("accept new client failure:%s\n",strerror(errno));
                    continue;
                }
                event.data.fd = clifd;
                event.events =EPOLLIN;
                if(epoll_ctl(epollfd,EPOLL_CTL_ADD,clifd, &event) < 0)
                {
                    printf("epoll_add client socket failure:%s\n",strerror(errno));
                    close(event_array[i].data.fd);
                    continue;
                }
                printf("epoll add new client socket[%d] ok\n",clifd);
            }
            else
            {
                if( (rv=read(event_array[i].data.fd, buf, sizeof(buf))) <=0)
                {
                    printf("socket[%d] read failure or get disconnected and will be removed\n",event_array[i].data.fd);
                    epoll_ctl(epollfd, EPOLL_CTL_DEL,event_array[i].data.fd,NULL);
                    close(event_array[i].data.fd);
                    continue;
                }
                else
                {
                    printf("socket[%d] read get %d bytes data\n", event_array[i].data.fd, rv);
                      /*  convert letter from lowercase to uppercase */
                    for(j=0; j<rv; j++)
                        buf[j]=toupper(buf[j]);
                    if( write(event_array[i].data.fd, buf, rv) < 0 )
                    {
                           printf("socket[%d] write failure: %s\n", event_array[i].data.fd, strerror(errno));
                           epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
                           close(event_array[i].data.fd);
                   }
                        }
                 }
             } 
         } 
CleanUp:
    close(listenfd);
return 0;
}

int socket_Server_init(char *listen_ip, int listen_port)
{

    struct sockaddr_in  servaddr;
    int     rv = 0;
    int     on = 1;
    int     listenfd;
    
    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("use socket()to create a TCP socket failure:%s\n",strerror(errno));
                return -1;
    }
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port = htons(listen_port);

    if( !listen_ip )
    {
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    }
    else
    {
        if(inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <=0)
        {
            printf("inet_pton set listen IP address failure\n");
            rv = -2;
            goto cleanup;
        }
    }
    
    if( bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
    {
        printf("socket[%d] bind to port failure:%s\n",listenfd,strerror(errno));
        rv = -3;
        goto cleanup;
    }
      
    if( listen(listenfd,13) < 0)
    {
         printf("use bind to bind tcp socket failure:%s\n",strerror(errno));
         rv = -4;
         goto cleanup;
    }

    printf("it is makun\n");
cleanup:
    if(rv < 0)
        close(listenfd);
    else
        rv = listenfd;

    return rv;
}
void set_socket_rlimit(void)
{
     struct rlimit limit = {0};
     getrlimit(RLIMIT_NOFILE, &limit );
     limit.rlim_cur = limit.rlim_max;
     setrlimit(RLIMIT_NOFILE, &limit );
     printf("set socket open fd max count to %d\n", limit.rlim_max);
}

